# -*- coding: utf-8 -*-
#
# Author: Andrzej Rutkowski (andrzej.rutkowski+code (at) gmail.com)
#
# Tag Version 0.0 (pre-alpha)
# This program can be distributed under the terms of the GNU GPL.
# See the file COPYING.
#

import os, sys
import shutil
try:
	import sqlite3 as sqlite
except ImportError:
	from pysqlite2 import dbapi2 as sqlite  # NOTTESTED

class TagSystem(object):
	def __init__(self):
		self.dbconn = None
		configPath = os.path.expanduser("~/.tagutil")
		if not os.path.exists(configPath):
			try:
				os.mkdir(configPath)
			except OSError:
				exit(-1)
		elif not os.path.isdir(configPath):
			exit(-1)
		self.dbPath = configPath + "/maindb.sqlite"

		if not os.path.exists(self.dbPath):
			installDB = True
		else:
			installDB = False

		self.open()

		if installDB:
			self.dbconn.executescript("""	
				CREATE TABLE IF NOT EXISTS files (
						id INTEGER PRIMARY KEY,
						path TEXT
						);
				CREATE TABLE IF NOT EXISTS tags (
						id INTEGER PRIMARY KEY,
						label TEXT
						);
				CREATE TABLE IF NOT EXISTS map (
						fid INTEGER,
						tid INTEGER
						);
				CREATE INDEX IF NOT EXISTS mapfiles ON map (fid);
				CREATE INDEX IF NOT EXISTS maptags ON map (tid);
				CREATE INDEX IF NOT EXISTS tagslabel ON tags (label);
				CREATE INDEX IF NOT EXISTS filespath ON files (path);
				CREATE TABLE IF NOT EXISTS config (
						id INTEGER PRIMARY KEY,
						lasttag TEXT
				);
				INSERT INTO config (id, lasttag) values (1, '');
			""");

	def open(self):
		self.dbconn = sqlite.connect(self.dbPath)

	def close(self):
		self.dbconn.commit()
		self.dbconn.close()
		self.dbconn = None

	def tagFile(self, path, tag):
		if tag in (None, ".", "*"):
			return
		
		c = self.dbconn.cursor()
		c.execute('select id from files where path=?', (path,))
		try:
			fid = c.next()[0]
		except StopIteration:
			c.execute('insert into files (path) values (?)', (path,))
			fid = c.lastrowid
		
		tagid = None
		c.execute('select id from tags where label=?', (tag,))
		try:
			tid = c.next()[0]
		except StopIteration:
			c.execute('insert into tags (label) values (?)', (tag,))
			tid = c.lastrowid
		
		c.execute('select * from map where fid=? and tid=?', (fid, tid))
		try:
			c.next()
		except StopIteration:
			c.execute('insert into map (fid, tid) values (?, ?)', (fid, tid))

	def untagFile(self, path, tag):
		c = self.dbconn.cursor()
		c.execute('select id from files where path=?', (path,))
		try:
			fid = c.next()[0]
		except StopIteration:
			return

		if tag != '*':
			c.execute('select id from tags where label=?', (tag,))
			try:
				tid = c.next()[0]
			except StopIteration:
				return
			c.execute('delete from map where fid=? and tid=?', (fid, tid))
		
			c.execute('select * from map where fid=?', (fid,))
			try:
				c.next()
			except StopIteration:
				c.execute('delete from files where id=?', (fid,))
			c.execute('select * from map where tid=?', (tid,))
			try:
				c.next()
			except StopIteration:
				c.execute('delete from tags where id=?', (tid,))
		else:
			c.execute('delete from map where fid=?', (fid,))
			c.execute('delete from files where id=?', (fid,))
			# nie usuwa samotnych tagów!!!

	def untagAllFiles(self, tag):
		c = self.dbconn.cursor()
		c.execute('select id from tags where label=?', (tag,))
		try:
			tid = c.next()[0]
		except StopIteration:
			return
		c.execute('select fid from map where tid=?', (tid,))
		d = self.dbconn.cursor()
		for fid in c:
			d.execute('delete from map where fid=? and tid=?', (fid[0], tid))
			d.execute('select * from map where fid=?', (fid[0],))
			try:
				d.next()
			except StopIteration:
				d.execute('delete from files where id=?', (fid[0],))
		c.execute('delete from tags where id=?', (tid,))

	def getFilesByTag(self, tag):
		c = self.dbconn.cursor()
		c.execute('select id from tags where label=?', (tag,))
		try:
			tid = c.next()[0]
		except StopIteration:
			return []
		c.execute('select files.path from map, files on map.fid = files.id where map.tid = ?', (tid,))
		return map(lambda l: l[0], c)

	def getFilesByTags(self, tags):
		fileset = set()
		for tag in tags:
			fileset.update(self.getFilesByTag(tag))
		return fileset

	def getFilesIdsByTags(self, tags):
		c = self.dbconn.cursor()
		fileset = set()
		for tag in tags:
			c.execute('select id from tags where label=?', (tag,))
			try:
				tid = c.next()[0]
			except StopIteration:
				continue
			c.execute('select files.id, files.path from map, files on map.fid = files.id where map.tid = ?', (tid,))
			for file in c:
				t = os.path.basename(str(t))
				dotpos = t.findr(".")
				if dotpos == -1:
					t = file[0]+"."+t
				else:
					t = t[:dotpos]+"."+str(file[0])+t[dotpos:]
				fileset.add(t)
		return fileset

	def mvFilesByTag(self, tag, destPath):
		if not os.path.isdir(destPath):
			return -2
		if destPath[-1] != '/':
			destPath += '/';
		c = self.dbconn.cursor()
		c.execute('select id from tags where label=?', (tag,))
		try:
			tid = c.next()[0]
		except StopIteration:
			return -1
		c.execute('select files.id, files.path from map, files on map.fid = files.id where map.tid = ?', (tid,))
		u = self.dbconn.cursor()
		for file in c:
			newPath = destPath + os.path.basename(file[1])
			shutil.move(file[1], newPath)
			u.execute('update files set path = ? where id = ?', (newPath, file[0]))

	def copyFilesByTag(self, tag, destPath):
		if not os.path.isdir(destPath):
			return -2
		if destPath[-1] != '/':
			destPath += '/';
		c = self.dbconn.cursor()
		c.execute('select id from tags where label=?', (tag,))
		try:
			tid = c.next()[0]
		except StopIteration:
			return -1
		c.execute('select files.id, files.path from map, files on map.fid = files.id where map.tid = ?', (tid,))
		u = self.dbconn.cursor()
		for file in c:
			newPath = destPath + os.path.basename(file[1])
			shutil.copy(file[1], newPath)

	def getTags(self):
		c = self.dbconn.cursor()
		c.execute('select label from tags')
		return map(lambda l: l[0], c)

	def getTagsByFile(self, path):
		c = self.dbconn.cursor()
		c.execute('select id from files where path=?', (path,))
		try:
			fid = c.next()[0]
		except StopIteration:
			return []
		c.execute('select tags.label from map, tags on map.tid = tags.id where map.fid = ?', (fid,))
		return map(lambda l: l[0], c)

	def getLastTag(self):
		c = self.dbconn.cursor()
		c.execute('select lasttag from config')
		try:
			return c.next()[0]
		except StopIteration:
			return None

	def setLastTag(self, tag):
		c = self.dbconn.cursor()
		c.execute('update config set lasttag = ? where id = 0', (tag, ))

